package com.dgut.lotterySystem.service.mq;


import cn.hutool.core.date.DateUtil;
import com.dgut.lotterySystem.common.pojo.exception.ServiceException;
import com.dgut.lotterySystem.common.utils.JacksonUtil;
import com.dgut.lotterySystem.common.utils.MailUtil;
import com.dgut.lotterySystem.common.utils.SMSUtil;
import com.dgut.lotterySystem.controller.param.DrawPrizeParam;
import com.dgut.lotterySystem.dao.dataObject.*;
import com.dgut.lotterySystem.dao.mapper.*;
import com.dgut.lotterySystem.service.activityStatus.impl.ActivityStatusMangerImpl;
import com.dgut.lotterySystem.service.dto.ConvertActivityStatusDTO;
import com.dgut.lotterySystem.service.enums.ActivityEnum;
import com.dgut.lotterySystem.service.enums.ActivityPrizeEnum;
import com.dgut.lotterySystem.service.enums.ActivityPrizeTierEnum;
import com.dgut.lotterySystem.service.enums.ActivityUserEnum;
import com.dgut.lotterySystem.service.impl.DrawPrizeServiceImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.dgut.lotterySystem.common.config.DirectRabbitConfig.QUEUE_NAME;

@Component
@RabbitListener(queues = QUEUE_NAME)
public class MQReceiver {
    public static final Logger logger= LoggerFactory.getLogger(MQReceiver.class);
    @Autowired
    private DrawPrizeServiceImpl drawPrizeService;
    @Autowired
    private ActivityStatusMangerImpl activityStatusManger;
    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;
    @Autowired
    private MailUtil mailUtil;
    @Autowired
    private SMSUtil smsUtil;
    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;
    @Autowired
    private WinningRecordMapper winningRecordMapper;
    @RabbitHandler
    public void process(Map<String,String> message) throws Exception {
        System.out.println("============================");
        //接受队列信息
        logger.info("MQ成功接收消息，message：{}", JacksonUtil.writeValueAsString(message));
        String paramString=message.get("messageData");
        DrawPrizeParam param=JacksonUtil.readValue(paramString,DrawPrizeParam.class);
        System.out.println(paramString);
        //处理抽奖流程

        try{

            System.out.println("=======================================================");
            //检验抽奖请求是否有效
            if(!drawPrizeService.checkDrawPrizeParam(param)){
                return;
            }
            //状态扭转处理
            statusConvert(param);
            //保存中奖名单
            List<WinningRecordDO> winningRecordDOList=drawPrizeService.saveWinningRecord(param);

            //采用异步并发（处理）
            //通知中奖这（邮箱，短信）
            syncExecute(winningRecordDOList);

        }catch (ServiceException e){
            logger.warn("处理MQ消息发送异常，异常码:{}，异常信息：{}，{}",e.getCode(),e.getMessage(),e);
            //需要保证事务一致性（回滚）
            //抛出异常：消息重试（解决异常：代码bug，网络问题，服务问题）
            rollBack(param);
            //抛出异常
            throw e;

        }catch (Exception e){
            logger.warn("处理MQ消息发生异常，{}",e);
            //需要保证事务一致性（回滚）
            //抛出异常
            rollBack(param);
            throw e;
        }

    }

    private void rollBack(DrawPrizeParam param) {
        //1.回滚活动，奖品，人员状态
        //判断是否需要回滚
        if(!needRollBackStatus(param)){
            //不需要返回
            return;
        }
        //需要，回滚
        rollBackStatus(param);


        //2。回滚中奖者列表
        //判断是否需要回滚
        if(!needRollBackWinner(param)){
            //不需要，返回
            return;
        }
        //需要，回滚
        rollBackWinner(param);

    }

    private void rollBackWinner(DrawPrizeParam param) {
        drawPrizeService.deleteRecords(param.getActivityId(),param.getPrizeId());
    }

    private boolean needRollBackWinner(DrawPrizeParam param) {
        //判断中奖记录表中是否要相关的中奖记录
        if(param.getActivityId()==null||param.getPrizeId()==null){
            logger.warn("查找中奖记录，参数，活动id和奖品id为空");
            return false;
        }
        int count=winningRecordMapper.selectByAPId(param.getActivityId(),param.getPrizeId());
        return count>0;
    }

    /**
     * 回滚设计活动的表的状态
     * @param param
     */
    private void rollBackStatus(DrawPrizeParam param) {
        ConvertActivityStatusDTO convertActivityStatusDTO=new ConvertActivityStatusDTO();
        convertActivityStatusDTO.setActivityId(param.getActivityId());
        convertActivityStatusDTO.setTargetActivityStatus(ActivityEnum.RUNNING);
        convertActivityStatusDTO.setPrizeId(param.getPrizeId());
        convertActivityStatusDTO.setTargetActivityPrizeStatus(ActivityPrizeEnum.INIT);
        convertActivityStatusDTO.setUserIds(param.getWinnerList().stream()
                .map(DrawPrizeParam.Winner::getUserId)
                .collect(Collectors.toList()));
        convertActivityStatusDTO.setTargetActivityUserStatus(ActivityUserEnum.INIT);

        activityStatusManger.rollBackHandleEvent(convertActivityStatusDTO);
    }

    /**
     * 判断是否状态是否需要回滚
     * @param param
     * @return
     */
    private boolean needRollBackStatus(DrawPrizeParam param) {
        //需要回滚活动，奖品，中奖人员状态
        //因为事务的一致性，所以只要判断奖品/人员状态是否转换，就知道是否需要回滚
        ActivityPrizeDO activityPrizeDO=activityPrizeMapper.SelectByActivityPrizeId(
                param.getActivityId(),param.getPrizeId());
        return activityPrizeDO.getStatus()
                .equalsIgnoreCase(
                        ActivityPrizeEnum.COMPLETED.name());
    }

    private void syncExecute(List<WinningRecordDO> winningRecordDOList) {

        //通过线程池 threadPoolTaskExecutor

        //可以拓展加入策略模式或者其他设计模式来完成后续的异步操作
        //短信通知
        threadPoolTaskExecutor.execute(()->sendMessage(winningRecordDOList));
        //邮箱通知
        threadPoolTaskExecutor.execute(()->sendMail(winningRecordDOList));
    }

    /**
     * 发邮件
     * @param winningRecordDOList
     */
    private void sendMail(List<WinningRecordDO> winningRecordDOList) {
        if(CollectionUtils.isEmpty(winningRecordDOList)){
            logger.info("中奖列表为空，不同发邮件");
            return;
        }
        for(WinningRecordDO winningRecordDO:winningRecordDOList){
            String content="Hi,"+winningRecordDO.getWinnerName()+"。恭喜你在"+winningRecordDO.getActivityName()
                    +"活动中获得"+ ActivityPrizeTierEnum.forName(winningRecordDO.getPrizeTier()).getMessage()
                    +":"+winningRecordDO.getPrizeName()+"。"
                    +"获奖时间为"+ DateUtil.formatTime(winningRecordDO.getWinningTime()) +"。请尽快领取你的奖品！";
            mailUtil.sendSampleMail(winningRecordDO.getWinnerEmail(),"中奖通知",content);
        }

    }

    /**
     * 发短信
     * @param winningRecordDOList
     */
    private void sendMessage(List<WinningRecordDO> winningRecordDOList) {
        if(CollectionUtils.isEmpty(winningRecordDOList)){
            logger.info("中奖列表为空，不同发邮件");
            return;
        }
        for(WinningRecordDO winningRecordDO:winningRecordDOList){
            Map<String,String> map=new HashMap<>();
//            map.put();
//            map.put();
//            map.put();
//            map.put();
//            map.put();
            smsUtil.sendMessage("",
                    winningRecordDO.getWinnerPhoneNumber().getValue(),
                    JacksonUtil.writeValueAsString(map));
        }
    }


    private void statusConvert(DrawPrizeParam param) {
        ConvertActivityStatusDTO dto=new ConvertActivityStatusDTO();
        dto.setActivityId(param.getActivityId());
        dto.setTargetActivityStatus(ActivityEnum.COMPLETED);
        dto.setPrizeId(param.getPrizeId());
        dto.setTargetActivityPrizeStatus(ActivityPrizeEnum.COMPLETED);
        dto.setUserIds(param.getWinnerList()
                .stream()
                .map(DrawPrizeParam.Winner::getUserId)
                .collect(Collectors.toList()));
        dto.setTargetActivityUserStatus(ActivityUserEnum.COMPLETED);
        activityStatusManger.handleEvent(dto);
    }

//    private void statusConvert(DrawPrizeParam param) {
//
//        //问题
//        //活动状态扭转依赖奖品状态，扩展性差，可维护性差
//        //解决方案：设计模式（责任链设计模式，策略模式）
//
//        //活动：Init->completed ,必须所有的奖品都是completed状态才能扭转
//        //奖品：Init->completed
//        //人员: Init->completed
//
//        //扭转奖品转态
//        //查询奖品信息
//        //判断条件：奖品状态是否是Init状态，是，扭转成Completed状态，不是，参数异常
//        //
//        //扭转人员状态
//        //查询人员信息
//        //判断条件：奖品状态是否是Init状态，是，扭转成Completed状态，不是，参数异常
//        //
//        //扭转活动转态
//        //查询活动信息（扭转依赖奖品信息）
//        //判断条件：奖品状态是否是Init状态，并且所有的奖品都是completed状态，才扭转成Completed状态，不是，参数异常
//
//        //更新redis的完整完整信息
//
//
//    }
}
